package org.example.config;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.example.entity.RestBean;
import org.example.entity.dto.Account;
import org.example.entity.vo.response.AuthorizeVO;
import org.example.filter.JwtAuthorizeFilter;
import org.example.service.AccountService;
import org.example.utils.JwtUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import java.io.PrintWriter;

@Configuration
public class SecurityConfiguration {
    @Resource
    JwtUtil jwtUtil;
    @Resource
    JwtAuthorizeFilter jwtAuthorizeFilter;
    @Resource
    AccountService accountService;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(conf -> conf
                        .requestMatchers(new AntPathRequestMatcher("/api/auth/**"), new AntPathRequestMatcher("/error")).permitAll()
                        .anyRequest().authenticated()
                )
                .formLogin(conf -> conf
                        .loginProcessingUrl("/api/auth/login")
                        .successHandler(((request, response, authentication) -> {
                            setJsonAndCharacter(response);
                            User user = (User) authentication.getPrincipal();
                            Account account = accountService.findAccountByNameOrEmail(user.getUsername());
                            String token = jwtUtil.createJwt(user, account.getId(), account.getUsername());
                            AuthorizeVO authorizeVO = account.asViewObject(AuthorizeVO.class, v -> {
                                v.setToken(token);
                                v.setExpire(jwtUtil.expireTime());
                            });
                            response.getWriter().write(RestBean.success(authorizeVO).asJsonString());
                        }))
                        .failureHandler(((request, response, exception) -> {
                            setJsonAndCharacter(response);
                            response.getWriter().write(RestBean.unauthorized(exception.getMessage()).asJsonString());
                        }))
                )
                .logout(conf -> conf
                        .logoutUrl("/api/auth/logout")
                        .logoutSuccessHandler(((request, response, authentication) -> {
                            setJsonAndCharacter(response);
                            PrintWriter writer = response.getWriter();
                            String authorization = request.getHeader("Authorization");
                            if (jwtUtil.invalidateJwt(authorization)) {
                                writer.write(RestBean.success().asJsonString());
                            } else {
                                writer.write(RestBean.failure(400, "退出登入失败").asJsonString());
                            }
                        }))
                )
                .csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(conf -> conf
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                )
                .exceptionHandling(conf -> conf
                        .authenticationEntryPoint(((request, response, authException) -> {
                            setJsonAndCharacter(response);
                            response.getWriter().write(RestBean.unauthorized(authException.getMessage()).asJsonString());
                        }))
                        .accessDeniedHandler(((request, response, accessDeniedException) -> {
                            setJsonAndCharacter(response);
                            response.getWriter().write(RestBean.forbidden(accessDeniedException.getMessage()).asJsonString());
                        })))
                .addFilterBefore(jwtAuthorizeFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }

    private void setJsonAndCharacter(HttpServletResponse response) {
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
    }

}
